// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


#include "CaeHttps.h"

#include "CaeHeader.h"
#include <regex>

#include "CaeSignUtil.h"
#include "../hwsecure/securec.h"

using namespace std;
const int CURL_TIME_OUT = 10000;
long CaeHttps::m_httpCode = 0L;

size_t CaeHttps::ResponseCallback(const void *contents, size_t size, size_t memBlockNum, void *sourcePtr)
{
    if (contents == nullptr || strlen((char *)contents) == 0 || size * memBlockNum == 0) {
        LOGE("Response is empty.");
        return 0;
    }
    if (sourcePtr == nullptr) {
        LOGE("ResponseCallback error, sourcePtr is null.");
        return 0;
    }

    auto *response = static_cast<HttpsResponse *>(sourcePtr);
    size_t realSize = size * memBlockNum;
    if (realSize > BUF_SIZE) {
        realSize = BUF_SIZE;
    }
    if (memcpy_s(response->buffer, realSize, contents, realSize) != EOK) {
        LOGE("Failed to copy memory.");
        return 0;
    }
    response->size = static_cast<int>(realSize);
    return realSize;
}

CaeHttps::CaeHttps()
{
    m_sha256withRSASigner = nullptr;
}

CaeHttps::~CaeHttps()
{
    CloseHttpsClient();
}

int CaeHttps::Init()
{
#if ENABLE_SIGNATURE
    if (this->m_sha256withRSASigner == nullptr) {
        this->m_sha256withRSASigner = new CaeSignUtil();
        if (this->m_sha256withRSASigner == nullptr) {
            LOGE("Init error, failed to new Sha256withRSASigner.");
            return -1;
        }
    }
#endif
    return 0;
}

bool CaeHttps::HttpsPost(CaeRequestParams *request, HttpsResponse &response, time_t validDateTime)
{
    CURL *httpsCurl = curl_easy_init();
    CURLcode res;
    request->AddHeader("content-type", "application/json");
    request->AddHeader("charset", "UTF-8");

#if ENABLE_SIGNATURE
    if (!m_sha256withRSASigner->InitSha256withRSASigner()) {
        LOGE("InitSha256withRSASigner failed.");
        curl_easy_cleanup(httpsCurl);
        return false;
    }
    if (!(this->m_sha256withRSASigner->CreateSignature(request, validDateTime))) {
        LOGE("CreateSignature failed.");
        curl_easy_cleanup(httpsCurl);
        return false;
    }
#endif

    curl_easy_setopt(httpsCurl, CURLOPT_CUSTOMREQUEST, request->GetMethod().c_str());
    std::string url = request->GetUseUrl() ? request->GetUrl() : "https://" + request->GetHost() +
                                                                 request->GetUri();
    curl_easy_setopt(httpsCurl, CURLOPT_URL, url.c_str());
    struct curl_slist *chunk = nullptr;
    std::set<Header>::iterator it;
    for (auto header : *request->GetHeaders()) {
        std::string headerEntry = header.GetKey() + ": " + header.GetValue();
        chunk = curl_slist_append(chunk, (const char *)headerEntry.c_str());
    }
    curl_easy_setopt(httpsCurl, CURLOPT_HTTPHEADER, chunk);
    curl_easy_setopt(httpsCurl, CURLOPT_POST, 1L);
    curl_easy_setopt(httpsCurl, CURLOPT_COPYPOSTFIELDS,
                     request->GetPayload().c_str());                   // 设置发送的数据内容
    curl_easy_setopt(httpsCurl, CURLOPT_NOBODY, 0L);
    curl_easy_setopt(httpsCurl, CURLOPT_WRITEFUNCTION, ResponseCallback);  // 设置回调函数
    curl_easy_setopt(httpsCurl, CURLOPT_WRITEDATA, (void *)&response); // 设置回调信息接收指针
    curl_easy_setopt(httpsCurl, CURLOPT_SSL_VERIFYPEER, 0L);           // 设置免验证对端证书
    curl_easy_setopt(httpsCurl, CURLOPT_SSL_VERIFYHOST, 0L);           // 设置免验证服务器证书
    curl_easy_setopt(httpsCurl, CURLOPT_PROXY_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(httpsCurl, CURLOPT_PROXY_SSL_VERIFYHOST, 0L);
    curl_easy_setopt(httpsCurl, CURLOPT_VERBOSE, true);                // 打印运行日志
    curl_easy_setopt(httpsCurl, CURLOPT_FORBID_REUSE, true);           // 端口可重用
    curl_easy_setopt(httpsCurl, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt(httpsCurl, CURLOPT_TCP_KEEPALIVE, 1L);
    curl_easy_setopt(httpsCurl, CURLOPT_TCP_KEEPIDLE, 120L);
    curl_easy_setopt(httpsCurl, CURLOPT_TCP_KEEPINTVL, 60L);
    curl_easy_setopt(httpsCurl, CURLOPT_TIMEOUT_MS, CURL_TIME_OUT);
    res = curl_easy_perform(httpsCurl);
    if (res != CURLE_OK) {
        LOGE("HttpsPost curl_easy_perform() failed: %s", curl_easy_strerror(res));
        curl_easy_cleanup(httpsCurl);
        return false;
    }
    curl_easy_getinfo(httpsCurl, CURLINFO_RESPONSE_CODE, &m_httpCode);
    response.httpCode = m_httpCode;
    LOGD("Send Https_request end.");
    curl_easy_cleanup(httpsCurl);
    return true;
}

bool CaeHttps::HttpsDownload(CaeRequestParams *request, const std::string &outputFile)
{
    CURL *httpsCurl = curl_easy_init();
    CURLcode res;
    request->AddHeader("content-type", "application/json");
    std::string url = request->GetUseUrl() ? request->GetUrl() :
                      "https://" + request->GetHost() + request->GetUri() + "?" + request->GetQueryParams();
    FILE *fp = fopen(outputFile.c_str(), "wb");
    if (!fp) {
        return false;
    }
    curl_easy_setopt(httpsCurl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(httpsCurl, CURLOPT_WRITEFUNCTION, NULL);
    curl_easy_setopt(httpsCurl, CURLOPT_WRITEDATA, fp); // 设置回调信息接收指针
    curl_easy_setopt(httpsCurl, CURLOPT_SSL_VERIFYPEER, 0L);           // 设置免验证对端证书
    curl_easy_setopt(httpsCurl, CURLOPT_SSL_VERIFYHOST, 0L);           // 设置免验证服务器证书
    curl_easy_setopt(httpsCurl, CURLOPT_PROXY_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(httpsCurl, CURLOPT_PROXY_SSL_VERIFYHOST, 0L);
    curl_easy_setopt(httpsCurl, CURLOPT_VERBOSE, true);                // 打印运行日志
    curl_easy_setopt(httpsCurl, CURLOPT_FORBID_REUSE, true);           // 端口可重用
    curl_easy_setopt(httpsCurl, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt(httpsCurl, CURLOPT_TCP_KEEPALIVE, 1L);
    curl_easy_setopt(httpsCurl, CURLOPT_TCP_KEEPIDLE, 120L);
    curl_easy_setopt(httpsCurl, CURLOPT_TCP_KEEPINTVL, 60L);
    curl_easy_setopt(httpsCurl, CURLOPT_TIMEOUT_MS, CURL_TIME_OUT);
    curl_easy_setopt(httpsCurl, CURLOPT_PROXY_SSL_VERIFYHOST, 0L);

    int index = 0;
    while (index < 10) {
        res = curl_easy_perform(httpsCurl);
        if (res == CURLE_OK) {
            long status;
            curl_easy_getinfo(httpsCurl, CURLINFO_HTTP_CODE, &status);
            if (status != 200) {
                LOGE("Failed to access download url. code = %ld. ", status);
                fclose(fp);
                fp = nullptr;
                remove(outputFile.c_str());
                return false;
            }
            fclose(fp);
            fp = nullptr;
            return true;
        } else {
            index++;
            LOGE("HttpsDownload curl_easy_perform() failed: %s, %dth retry...", curl_easy_strerror(res),
                 index);
            usleep(100);
        }
    }
    fclose(fp);
    fp = nullptr;
    return false;
}

void CaeHttps::CloseHttpsClient() noexcept
{
#if ENABLE_SIGNATURE
    if (this->m_sha256withRSASigner != nullptr) {
        delete m_sha256withRSASigner;
        m_sha256withRSASigner = nullptr;
    }
#endif
}
